﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using GameLogic;
using FiveInARow.Components;
using System.Threading.Tasks;
using System.Windows;

namespace FiveInARow.Views
{
	public partial class MainViewModel : INotifyPropertyChanged
	{
		#region .ctors

		public MainViewModel()
		{
			Board = Board.CreateNew(51, 51);
			IndexGetter = (o) => 
				{
					if (o is Vertice) return (o as Vertice).Index;
					if (o is KeyValuePair<Tuple<int, int>, Vertice>)
					{
						return ((KeyValuePair<Tuple<int, int>, Vertice>)o).Value.Index;
					}
					throw new NotSupportedException();
				};
			SetValueCommand = new DelegateCommand<Vertice>(SetValue);
            StartGameCommand = new DelegateCommand<object>(StartGame);
            RefreshCommand = new DelegateCommand<object>(Refresh);

            StartGame(null);
		}

		#endregion .ctors

		#region Properties

        /// <summary>
        /// Gets/sets ShowProbabilities.
        /// </summary>
        public bool ShowProbabilities
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return p_ShowProbabilities; }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (p_ShowProbabilities != value)
                {
                    p_ShowProbabilities = value;
                    OnPropertyChanged("ShowProbabilities");
                    OnShowProbabilitiesChanged();
                }
            }
        }
        private bool p_ShowProbabilities;
        partial void OnShowProbabilitiesChanged();

		/// <summary>
		/// Gets/sets IndexGetter.
		/// </summary>
		public Func<object, Tuple<int, int>> IndexGetter
		{
			[System.Diagnostics.DebuggerStepThrough]
			get { return p_IndexGetter; }
			[System.Diagnostics.DebuggerStepThrough]
			set
			{
				if (p_IndexGetter != value)
				{
					p_IndexGetter = value;
					OnPropertyChanged("IndexGetter");
					OnIndexGetterChanged();
				}
			}
		}
		private Func<object, Tuple<int, int>> p_IndexGetter;
		partial void OnIndexGetterChanged();

		/// <summary>
		/// Gets/sets Board.
		/// </summary>
		public Board Board
		{
			[System.Diagnostics.DebuggerStepThrough]
			get { return p_Board; }
			[System.Diagnostics.DebuggerStepThrough]
			set
			{
				if (p_Board != value)
				{
					p_Board = value;
					OnPropertyChanged("Board");
					OnBoardChanged();
				}
			}
		}
		private Board p_Board;
		partial void OnBoardChanged();

		/// <summary>
		/// Gets/sets Graph.
		/// </summary>
		public Graph Graph
		{
			[System.Diagnostics.DebuggerStepThrough]
			get { return p_Graph; }
			[System.Diagnostics.DebuggerStepThrough]
			set
			{
				if (p_Graph != value)
				{
					p_Graph = value;
					OnPropertyChanged("Graph");
					OnGraphChanged();
				}
			}
		}
		private Graph p_Graph;
		partial void OnGraphChanged();

		/// <summary>
		/// Gets/sets SetValueCommand.
		/// </summary>
		public DelegateCommand<Vertice> SetValueCommand
		{
			[System.Diagnostics.DebuggerStepThrough]
			get { return p_SetValueCommand; }
			[System.Diagnostics.DebuggerStepThrough]
			set
			{
				if (p_SetValueCommand != value)
				{
					p_SetValueCommand = value;
					OnPropertyChanged("SetValueCommand");
					OnSetValueCommandChanged();
				}
			}
		}
		private DelegateCommand<Vertice> p_SetValueCommand;
		partial void OnSetValueCommandChanged();

		/// <summary>
		/// Gets/sets Offset.
		/// </summary>
		public Tuple<double, double> Offset
		{
			[System.Diagnostics.DebuggerStepThrough]
			get { return p_Offset; }
			[System.Diagnostics.DebuggerStepThrough]
			set
			{
				if (p_Offset != value)
				{
					p_Offset = value;
					OnPropertyChanged("Offset");
					OnOffsetChanged();
				}
			}
		}
		private Tuple<double, double> p_Offset;
		partial void OnOffsetChanged();

        /// <summary>
        /// Gets/sets State.
        /// </summary>
        public GameState State
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return p_State; }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (p_State != value)
                {
                    p_State = value;
                    OnPropertyChanged("State");
                    OnStateChanged();
                }
            }
        }
        private GameState p_State;
        partial void OnStateChanged();

        /// <summary>
        /// Gets/sets Winner.
        /// </summary>
        public CellValue Winner
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return p_Winner; }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (p_Winner != value)
                {
                    p_Winner = value;
                    OnPropertyChanged("Winner");
                    OnWinnerChanged();
                }
            }
        }
        private CellValue p_Winner;
        partial void OnWinnerChanged();

        /// <summary>
        /// Gets/sets StartGameCommand.
        /// </summary>
        public DelegateCommand<object> StartGameCommand
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return p_StartGameCommand; }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (p_StartGameCommand != value)
                {
                    p_StartGameCommand = value;
                    OnPropertyChanged("StartGameCommand");
                    OnStartGameCommandChanged();
                }
            }
        }
        private DelegateCommand<object> p_StartGameCommand;
        partial void OnStartGameCommandChanged();

        /// <summary>
        /// Gets/sets RefreshCommand.
        /// </summary>
        public DelegateCommand<object> RefreshCommand
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return p_RefreshCommand; }
            [System.Diagnostics.DebuggerStepThrough]
            set
            {
                if (p_RefreshCommand != value)
                {
                    p_RefreshCommand = value;
                    OnPropertyChanged("RefreshCommand");
                    OnRefreshCommandChanged();
                }
            }
        }
        private DelegateCommand<object> p_RefreshCommand;
        partial void OnRefreshCommandChanged();

		#endregion Properties

		#region Methods

		public event PropertyChangedEventHandler PropertyChanged;

		protected void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		private void SetValue(Vertice vertice)
		{
            if (vertice.IsEmpty())
            {
                Task.Factory.StartNew(() =>
                {
                    var result = Board.Set(vertice);
                    if (result != Option<CellValue>.None)
                    {
                        Application.Current.Dispatcher.BeginInvoke((Action<CellValue>)ShowPlayerHasWonMessage, ((Option<CellValue>.Some)result).Item);
                    }
                    else
                    {
                        if (ShowProbabilities)
                        {
                            Graph = Board.HighlightBestMoves();
                        }
                    }
                });
            }
		}

		partial void OnOffsetChanged()
		{
		}

        partial void OnShowProbabilitiesChanged()
        {
            if (ShowProbabilities)
            {
                Graph = Board.HighlightBestMoves();
            }
        }

        private void ShowPlayerHasWonMessage(CellValue winner)
        {
            State = GameState.Stopped;
            Winner = winner;
        }

        private void StartGame(object o)
        {
            State = GameState.InProgress;
            Offset = new Tuple<double, double>(-Constants.CellWidth * 0, -Constants.CellWidth * 0);
            Board.Clear();
            Graph = null;
        }

        private void Refresh(object o)
        {
            Graph = Board.HighlightBestMoves();
        }

		#endregion Methods
	}
}
